﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;

using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Storage;
using Microsoft.Extensions.Logging.Abstractions;
using SW.DDD.Domain;

namespace SW.DDD.Infrastructure
{
    public class EFUnitOfWork<TDbContext> : IUnitOfWork
        where TDbContext : DbContext
    {
        public EFUnitOfWork(TDbContext dbContext)
        {
            DbContext = dbContext ?? throw new ArgumentNullException(nameof(dbContext));
        }

        protected TDbContext DbContext { get; set; }

        public virtual async Task<IEnumerable<IEntity>> SaveChangesAsync(CancellationToken cancellationToken = default)
        {
            var c = await DbContext.SaveChangesAsync(cancellationToken);
            return DbContext.ChangeTracker
                            .Entries()
                            .Select(etr => etr.Entity)
                            .Where(e => e is IEntity)
                            .Cast<IEntity>();
        }

        public virtual Task BeginTransactionAsync(CancellationToken cancellationToken = default)
        {
            return DbContext.Database.BeginTransactionAsync(cancellationToken);
        }

        public virtual Task CommitTransactionAsync(CancellationToken cancellationToken = default)
        {
            DbContext.Database.CommitTransaction();
            return Task.CompletedTask;
        }

        public virtual Task RollbackTransactionAsync(CancellationToken cancellationToken = default)
        {
            DbContext.Database.RollbackTransaction();
            return Task.CompletedTask;
        }

        public Guid? GetCurrentTransactionId()
        {
            return DbContext.Database.CurrentTransaction?.TransactionId;
        }
    }
}
